//
//  Texture.cpp
//  RayTracer
//
//  Created by 俞云康 on 2/15/16.
//  Copyright © 2016 yushroom. All rights reserved.
//

#include "Texture.hpp"
#include "Params.hpp"
#include <fstream>

std::vector<shared_ptr<Texture>> Texture::textures;

shared_ptr<Texture> Texture::create(const Params& params) {
    string type = params.getType();
    string name = params.getID();
    string ref = params.getRef();
    if (ref != "") {
        for (auto& t : textures) {
            if (t->name == ref) {
                return t;
            }
        }
        error("ref texture(%s) not found\n", ref.c_str());
    }
    shared_ptr<Texture> t;
    if ("constant" == type) {
        t = ConstantTexture::create(params);
    } else if ("checkBoard" == type) {
        t = CheckBoard::create(params);
    } else if ("bitmap" == type) {
        t = Bitmap::create(params);
    } else {
        error("unkown Texture type: %s\n", type.c_str());
    }
    if (name != "") {
        for (auto& t : textures) {
            if (t->name == name) {
                error("duplicate texture name: %s\n", name.c_str());
            }
        }
    }
    t->name = name;
    textures.push_back(t);
    
    return t;
}

ConstantTexture::ConstantTexture(const Color& color)
    : color(color)
{
    
}

shared_ptr<Texture> ConstantTexture::create(const Params& params) {
    return make_shared<ConstantTexture>(params.getColor());
}


CheckBoard::CheckBoard(const Color& color1,
                       const Color& color2,
                       const int x_count,
                       const int y_count)
    :   color1(color1), color2(color2),
        x_count(x_count), y_count(y_count)
{
    inv_x_count = 1.0f / x_count;
    inv_y_count = 1.0f / y_count;
}


shared_ptr<Texture> CheckBoard::create(const Params& params) {
    int x_count = params.getInt("u_count");
    int y_count = params.getInt("v_count");
    Color c1 = params.getColor("color1");
    Color c2 = params.getColor("color2");
    return make_shared<CheckBoard>(c1, c2, x_count, y_count);
}


Bitmap::Bitmap(const int width,
               const int height,
               const vector<unsigned char>& pixels,
               const int bitsPerPixel,
               const bool verticalFlip)
    :mPixels(width*height), mWidth(width), mHeight(height)
{
    const int bytes = bitsPerPixel / 8;
    if (pixels.size() != bytes*width * height) {
        error("Bitmap::Bitmap, not enough pixels data!\n");
    }
    const unsigned char* src = pixels.data();
    
    if (!verticalFlip) {
        for (int i = 0; i < height*width; ++i) {
            mPixels[i].r = *(src+2) / 255.0f;
            mPixels[i].g = *(src+1) / 255.0f;
            mPixels[i].b = *(src+0) / 255.0f;
            src += bytes;
        }
    } else {
        for (int y = height-1; y >= 0; --y) {
            for(int x = 0; x < width; ++x) {
                int i = y*width + x;
                mPixels[i].r = *(src+2) / 255.0f;
                mPixels[i].g = *(src+1) / 255.0f;
                mPixels[i].b = *(src+0) / 255.0f;
                src += bytes;
            }
        }
    }
}


shared_ptr<Texture> Bitmap::create(const Params& params)
{
    string fn = params.getString("filename");
    ifstream fin(fn, std::ios::binary);
    unsigned char info[54];
    fin.read((char*)info, 54); // read the 54-byte header
    
    // extract image height and width from header
    int width = *(int*)&info[18];
    int height = *(int*)&info[22];
    int bitsPerPixel = *(int*)&info[28];
    if (bitsPerPixel!=24) {
        error("This program is for 24bpp files. Your bmp is not that\n");
        abort();
    }
    int compressionMethod = info[30];
    if (compressionMethod!=0) {
        error("There's some compression stuff going on that we might not be able to deal with.\n");
        error("Comment out offending lines to continue anyways. bpm.h line: %d\n", __LINE__);
        abort();
    }
    bool flip = false;
    if (height < 0) {
        height = -height;
        flip = true;
    }
    int size = bitsPerPixel/8 * width * height; // allocate 3 bytes per pixel
    vector<unsigned char> data(size);
    fin.read((char*)data.data(), size);
    return make_shared<Bitmap>(width, height, data, bitsPerPixel, flip);
}


Color Bitmap::sample(float u, float v)
{
    const int x = round2Int(u*(mWidth-1));
    const int y = round2Int(v*(mHeight-1));
    return mPixels[y*mWidth + x];
    //return Color(1, 0.8, 0.8);
}

